#include <iostream>
# include <string>
#include "checker.h"
#include "Gomoku.h"
#include "Board.h"

//# define USE_CHECKER

BoardCase const Board::WIDTH = 19;
BoardCase const Board::HEIGHT = 19;

Board::Board(int _width, int _height)
: m_cases(NULL), m_width(_width), m_height(_height)
{
	/*
#ifdef _DEBUG
# ifdef USE_CHECKER
	Checker::Instance().addNew(this);
# endif
#endif
	*/
	if (m_width <= 0 || m_width > Board::WIDTH)
		m_width = Board::WIDTH;
	if (m_height <= 0 || m_height > Board::HEIGHT)
		m_height = Board::HEIGHT;
	this->init();
}

Board::Board(Board const& motherBoard)
	: m_cases(NULL), m_width(motherBoard.Width), m_height(motherBoard.Height)
{
#ifdef _DEBUG
# ifdef USE_CHECKER
	Checker::Instance().addNew(this);
# endif
#endif
	m_cases = new BoardCase*[m_height];
	if (motherBoard.m_cases != NULL)
	{
		for (BoardCase y = 0; y < m_height; y++)
		{
			m_cases[y] = new BoardCase[m_width];
			if (motherBoard.m_cases[y] != NULL)
				for (BoardCase x = 0; x < m_width; x++)
					m_cases[y][x] = motherBoard.m_cases[y][x];
		}
	}
}

Board::Board(Board const& _source, int _x, int _y, int _width, int _height)
{
#ifdef _DEBUG
# ifdef USE_CHECKER
	Checker::Instance().addNew(this);
# endif
#endif
	if (_width <= 0 || _width > _source.Width) { _width = _source.Width; }
	if (_height <= 0 || _height > _source.Height) { _height = _source.Height; }
	if (_x < 0 || _x >= _source.Width) { _x = 0; }
	if (_y < 0 || _y >= _source.Height) { _y = 0; }

	m_width = _width;
	m_height = _height;

	m_cases = new BoardCase*[m_height];
	if (_source.m_cases != NULL)
	{
		for (BoardCase y = 0, srcy = _y; y < m_height; y++, srcy++)
		{
			m_cases[y] = new BoardCase[m_width];
			if (_source.m_cases[y] != NULL)
				for (BoardCase x = 0, srcx = _x; x < m_width; x++, srcx++)
					m_cases[y][x] = _source.m_cases[y][x];
		}
	}
}

void Board::init()
{
	if (m_cases != NULL)
	{
		for (BoardCase y = 0; y < m_height; y++)
			if (m_cases[y] != NULL)
				delete [] m_cases[y];
		delete [] m_cases;
	}
	m_cases = new BoardCase*[m_height];
	if (m_cases != NULL)
	{
		for (BoardCase y = 0; y < m_height; y++)
		{
			m_cases[y] = NULL;
			m_cases[y] = new BoardCase[m_width];
			if (m_cases[y] != NULL)
				for (BoardCase x = 0; x < m_width; x++)
				{
					m_cases[y][x] = SET_COLOR(COLOR_NONE);
					m_cases[y][x] += SET_POSITION_X(x);
					m_cases[y][x] += SET_POSITION_Y(y);
				}
		}
	}
}

Board::~Board()
{
#ifdef _DEBUG
# ifdef USE_CHECKER
	Checker::Instance().addDelete(this);
# endif
#endif
	if (m_cases != NULL)
	{
		for (BoardCase y = 0; y < m_height; y++)
			if (m_cases[y] != NULL)
				delete [] m_cases[y];
		delete [] m_cases;
	}
}

BoardCase** Board::getCases()
{
	return ((BoardCase**)m_cases);
}

int Board::getWidth() const
{
	return (m_width);
}

int Board::getHeight() const
{
	return (m_height);
}

void Board::setCases(BoardCase** _cases)
{
	m_cases = _cases;
}

void Board::PrintCase(BoardCase const& _case)
{
#ifdef _DEBUG
	static std::string color[] = { "BLACK", "WHITE", "NONE" };
	static std::string align[] = { "NONE", "UP", "DOWN", "LEFT", "RIGHT", "UP_LEFT", "UP_RIGHT", "DOWN_LEFT", "DOWN_RIGHT" };
	static std::string rule_three[] = { "NONE", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "HEIGHT", "NINE", "TEN", "ELEVEN", "TWELVE" };

	std::cout << "========================" << std::endl;
	std::cout << "Position: [" << CONVERT_POSITION_X(GET_POSITION_X(_case)) << ", " << CONVERT_POSITION_Y(GET_POSITION_Y(_case)) << "]" << std::endl;

	if (GET_COLOR(_case) < COLOR_COUNT)
		std::cout << "Color: " << color[GET_COLOR(_case)] << " (" << GET_COLOR(_case) << ")" << std::endl;
	else
		std::cout << "Color: Error" << " (" << GET_COLOR(_case) << ")" << std::endl;
	
	std::cout << "Ennemies: " << std::endl;
	if (GET_ALIGN_ENMY(_case) == ENMY_NONE)
		std::cout << "No align" << std::endl;
	else
	{
		BoardCase malign = GET_ALIGN_ENMY(_case);
		if (malign & ENMY_UP)
			std::cout << align[1] << std::endl;
		if (malign & ENMY_DOWN)
			std::cout << align[2] << std::endl;
		if (malign & ENMY_LEFT)
			std::cout << align[3] << std::endl;
		if (malign & ENMY_RIGHT)
			std::cout << align[4] << std::endl;
		if (malign & ENMY_UP_LEFT)
			std::cout << align[5] << std::endl;
		if (malign & ENMY_UP_RIGHT)
			std::cout << align[6] << std::endl;
		if (malign & ENMY_DOWN_LEFT)
			std::cout << align[7] << std::endl;
		if (malign & ENMY_DOWN_RIGHT)
			std::cout << align[8] << std::endl;

	}
	std::cout << "Allies: " << std::endl;
	if (GET_ALIGN_ALLY(_case) == ALLY_NONE)
		std::cout << "No align" << std::endl;
	else
	{
		BoardCase malign = GET_ALIGN_ALLY(_case);
		if (malign & ALLY_UP)
			std::cout << align[1] << std::endl;
		if (malign & ALLY_DOWN)
			std::cout << align[2] << std::endl;
		if (malign & ALLY_LEFT)
			std::cout << align[3] << std::endl;
		if (malign & ALLY_RIGHT)
			std::cout << align[4] << std::endl;
		if (malign & ALLY_UP_LEFT)
			std::cout << align[5] << std::endl;
		if (malign & ALLY_UP_RIGHT)
			std::cout << align[6] << std::endl;
		if (malign & ALLY_DOWN_LEFT)
			std::cout << align[7] << std::endl;
		if (malign & ALLY_DOWN_RIGHT)
			std::cout << align[8] << std::endl;
	}
	std::cout << "Surrounded: " << ((GET_SURROUNDED(_case) == IS_SURROUNDED) ? "true" : "false") << std::endl;
	std::cout << "========================" << std::endl;
#endif
}

bool Board::CanIMove(unsigned int _dest_x, unsigned int _dest_y, BoardCase _color, bool _verbose)
{
	Arbiter& arbiter = Gomoku::Instance().Arbiter;

	BoardCase tmp;
	Board::CreateNewBoardCase(tmp, _color, _dest_x, _dest_y, ENMY_NONE, ALLY_NONE, 0);
	bool result = arbiter.CanIMove(_dest_x, _dest_y, tmp, *this, _verbose);
	if (!result) { return (false); }
	Board::CreateNewBoardCase(m_cases[_dest_y][_dest_x], ((result == true) ? _color : COLOR_NONE), _dest_x, _dest_y, GET_ALIGN_ENMY(tmp), GET_ALIGN_ALLY(tmp), GET_SURROUNDED(tmp));

	this->UpdateCaseState(m_cases[_dest_y][_dest_x]);
	if (arbiter.IsFivePieceAligned(m_cases[_dest_y][_dest_x], *this, _verbose))
		arbiter.SetCurrentPlayerWin(true);
	return (true);
}

void Board::CapturePieces(BoardCase& _case)
{
	int x = 0, y = 0;
	Board::GetCasePosition(_case, x, y);
	int nbDirections = 8;
	BoardCase color = (GET_COLOR(_case) == COLOR_BLACK) ? COLOR_BLACK : COLOR_WHITE;
	Arbiter& arbiter = Gomoku::Instance().Arbiter;
	for (int i = 0; i < nbDirections; i++)
	{
		std::list<BoardCase*> pieces;
		if (arbiter.CanICapture(*this, _case, i + 1, pieces))
		{
			int offx = Board::GetXIncrementFromDirection(i + 1);
			int offy = Board::GetYIncrementFromDirection(i + 1);
			std::list<BoardCase*>::iterator it = pieces.begin();
			for (; it != pieces.end(); ++it)
			{
				Board::ClearCase(*(*it), x + offx, y + offy);
				offx += offx;
				offy += offy;
			}
			Gomoku& gomoku = Gomoku::Instance();
			gomoku.getPlayers()[color].EnnemyPiecesCount += 2;
		}
	}
}

bool Board::CheckIfSurrounded(BoardCase& _case)
{
	int nbDirections = 8;
	for (int i = 0; i < nbDirections; i++)
	{
		BoardCase ally = Board::ConvertDirectionToAlly(i + 1);
		BoardCase enmy = Board::ConvertDirectionToEnnemy(i + 1);
		if (!(GET_ALIGN_ALLY(_case) & ally) && !(GET_ALIGN_ENMY(_case) & enmy))
			return (false);
	}
	return (true);
}

void Board::ClearCase(BoardCase& _case, int _x, int _y)
{
	int nbDirections = 8;
	BoardCase color = GET_COLOR(_case);
	BoardCase advColor = (color == COLOR_BLACK) ? COLOR_WHITE : COLOR_BLACK;
	
	for (int i = 0; i < nbDirections; i++)
	{
		BoardCase* adjacentCase = this->GetAdjacentCase(_x, _y, (i + 1));
		if (adjacentCase != NULL)
		{
			BoardCase backup = *adjacentCase;
			if (GET_COLOR(*adjacentCase) == color)
			{
				BoardCase align = GET_ALIGN_ALLY(*adjacentCase);
				BoardCase direction = Board::ConvertDirectionToAlly(Board::GetOppositeDirection(i + 1));
				Board::CreateNewBoardCase(*adjacentCase, GET_COLOR(*adjacentCase),
					CONVERT_POSITION_X(GET_POSITION_X(*adjacentCase)), CONVERT_POSITION_Y(GET_POSITION_Y(*adjacentCase)),
					GET_ALIGN_ENMY(*adjacentCase), GET_ALIGN_ALLY(*adjacentCase) - direction, 0);
				
				BoardCase surounded = (Board::CheckIfSurrounded(*adjacentCase) == true) ? IS_SURROUNDED : 0;
				(*adjacentCase) += surounded;
			}
			else if (GET_COLOR(*adjacentCase) == advColor)
			{
				BoardCase align = GET_ALIGN_ENMY(*adjacentCase);
				BoardCase direction = Board::ConvertDirectionToEnnemy(Board::GetOppositeDirection(i + 1));
				Board::CreateNewBoardCase(*adjacentCase, GET_COLOR(*adjacentCase),
					CONVERT_POSITION_X(GET_POSITION_X(*adjacentCase)), CONVERT_POSITION_Y(GET_POSITION_Y(*adjacentCase)),
					GET_ALIGN_ENMY(*adjacentCase) - direction, GET_ALIGN_ALLY(*adjacentCase), 0);
				
				BoardCase surounded = (Board::CheckIfSurrounded(*adjacentCase) == true) ? IS_SURROUNDED : 0;
				(*adjacentCase) += surounded;
			}
		}
	}
	BoardCase tmp = _case;
	_case = COLOR_NONE;
	_case += SET_POSITION_X(_x);
	_case += SET_POSITION_Y(_y);
}


BoardCase* Board::GetAdjacentCase(int _x, int _y, int _direction)
{
	BoardCase** board = this->m_cases;

	switch (_direction)
	{
		case DIRECTION_LEFT:
			{
				if (_x - 1 >= 0)
					return (&board[_y][_x - 1]);
				else
					return (NULL);
			}
		case DIRECTION_RIGHT:
			{
				if (_x + 1 < m_width)
					return (&board[_y][_x + 1]);
				else
					return (NULL);
			}
		case DIRECTION_UP:
			{
				if (_y - 1 >= 0)
					return (&board[_y - 1][_x]);
				else
					return (NULL);
			}
		case DIRECTION_DOWN:
			{
				if (_y + 1 < m_height)
					return (&board[_y + 1][_x]);
				else
					return (NULL);
			}
		case DIRECTION_UP_LEFT:
			{
				if (_y - 1 >= 0 && _x - 1 >= 0)
					return (&board[_y - 1][_x - 1]);
				else
					return (NULL);
			}
		case DIRECTION_UP_RIGHT:
			{
				if (_y - 1 >= 0 && _x + 1 < m_width)
					return (&board[_y - 1][_x + 1]);
				else
					return (NULL);
			}
		case DIRECTION_DOWN_LEFT:
			{
				if (_y + 1 < m_height && _x - 1 >= 0)
					return (&board[_y + 1][_x - 1]);
				else
					return (NULL);
			}
		case DIRECTION_DOWN_RIGHT:
			{
				if (_y + 1 < m_height && _x + 1 < m_width)
					return (&board[_y + 1][_x + 1]);
				else
					return (NULL);
			}
		default: return (NULL);
	}
}

void Board::UpdateCaseState(BoardCase& _case, bool _updateAll)
{
	int nbDirections = 8;
	int x = 0, y = 0;
	BoardCase color = GET_COLOR(_case);
	BoardCase advColor = (color == COLOR_BLACK) ? COLOR_WHITE : COLOR_BLACK;

	Board::GetCasePosition(_case, x, y);
	
	for (int i = 0; i < nbDirections; i++)
	{
		BoardCase* adjacentCase = Board::GetAdjacentCase(x, y, (i + 1));
		if (adjacentCase != NULL)
		{
			if (GET_COLOR(*adjacentCase) == color)
			{
				_case += Board::ConvertDirectionToAlly(i + 1);
				if (_updateAll == true)
				{
					(*adjacentCase) += Board::ConvertDirectionToAlly(Board::GetOppositeDirection(i + 1));
				
					BoardCase surounded = (Board::CheckIfSurrounded((*adjacentCase)) == true) ? IS_SURROUNDED : 0;
					(*adjacentCase) += surounded;
				}
			}
			else if (GET_COLOR(*adjacentCase) == advColor)
			{
				_case += Board::ConvertDirectionToEnnemy(i + 1);
				if (_updateAll == true)
				{
					(*adjacentCase) += Board::ConvertDirectionToEnnemy(Board::GetOppositeDirection(i + 1));
				
					BoardCase surounded = (Board::CheckIfSurrounded((*adjacentCase)) == true) ? IS_SURROUNDED : 0;
					(*adjacentCase) += surounded;
				}
			}
		}
	}
}

#pragma region Converters

BoardCase Board::ConvertDirectionToAlly(int _direction)
{
	switch (_direction)
	{
		case DIRECTION_LEFT:		return (ALLY_LEFT);
		case DIRECTION_RIGHT:		return (ALLY_RIGHT);
		case DIRECTION_UP:			return (ALLY_UP);
		case DIRECTION_DOWN:		return (ALLY_DOWN);
		case DIRECTION_UP_LEFT:		return (ALLY_UP_LEFT);
		case DIRECTION_UP_RIGHT:	return (ALLY_UP_RIGHT);
		case DIRECTION_DOWN_LEFT:	return (ALLY_DOWN_LEFT);
		case DIRECTION_DOWN_RIGHT:	return (ALLY_DOWN_RIGHT);
		default: return (ALLY_NONE);
	}
}

BoardCase Board::ConvertDirectionToEnnemy(int _direction)
{
	switch (_direction)
	{
		case DIRECTION_LEFT:		return (ENMY_LEFT);
		case DIRECTION_RIGHT:		return (ENMY_RIGHT);
		case DIRECTION_UP:			return (ENMY_UP);
		case DIRECTION_DOWN:		return (ENMY_DOWN);
		case DIRECTION_UP_LEFT:		return (ENMY_UP_LEFT);
		case DIRECTION_UP_RIGHT:	return (ENMY_UP_RIGHT);
		case DIRECTION_DOWN_LEFT:	return (ENMY_DOWN_LEFT);
		case DIRECTION_DOWN_RIGHT:	return (ENMY_DOWN_RIGHT);
		default: return (ENMY_NONE);
	}
}

int Board::ConvertAllyToDirection(BoardCase _allyAlign)
{
	switch (_allyAlign)
	{
		case ALLY_LEFT:			return (DIRECTION_LEFT);
		case ALLY_RIGHT:		return (DIRECTION_RIGHT);
		case ALLY_UP:			return (DIRECTION_UP);
		case ALLY_DOWN:			return (DIRECTION_DOWN);
		case ALLY_UP_LEFT:		return (DIRECTION_UP_LEFT);
		case ALLY_UP_RIGHT:		return (DIRECTION_UP_RIGHT);
		case ALLY_DOWN_LEFT:	return (DIRECTION_DOWN_LEFT);
		case ALLY_DOWN_RIGHT:	return (DIRECTION_DOWN_RIGHT);
		default: return (0);
	}
}

int Board::ConvertEnnemyToDirection(BoardCase _ennemyAlign)
{
	switch (_ennemyAlign)
	{
		case ENMY_LEFT:			return (DIRECTION_LEFT);
		case ENMY_RIGHT:		return (DIRECTION_RIGHT);
		case ENMY_UP:			return (DIRECTION_UP);
		case ENMY_DOWN:			return (DIRECTION_DOWN);
		case ENMY_UP_LEFT:		return (DIRECTION_UP_LEFT);
		case ENMY_UP_RIGHT:		return (DIRECTION_UP_RIGHT);
		case ENMY_DOWN_LEFT:	return (DIRECTION_DOWN_LEFT);
		case ENMY_DOWN_RIGHT:	return (DIRECTION_DOWN_RIGHT);
		default: return (0);
	}
}

#pragma endregion

#pragma region Positions / Directions
int Board::GetXIncrementFromDirection(int _direction)
{
	switch (_direction)
	{
		case DIRECTION_LEFT:
		case DIRECTION_UP_LEFT:
		case DIRECTION_DOWN_LEFT:
			return (-1);
		case DIRECTION_RIGHT:
		case DIRECTION_UP_RIGHT:
		case DIRECTION_DOWN_RIGHT:
			return (1);
		case DIRECTION_UP:
		case DIRECTION_DOWN:
		default:
			return (0);
	}
}

int Board::GetYIncrementFromDirection(int _direction)
{
	switch (_direction)
	{
		case DIRECTION_UP:
		case DIRECTION_UP_LEFT:
		case DIRECTION_UP_RIGHT:
			return (-1);
		case DIRECTION_DOWN:
		case DIRECTION_DOWN_LEFT:
		case DIRECTION_DOWN_RIGHT:
			return (1);
		case DIRECTION_LEFT:
		case DIRECTION_RIGHT:
		default:
			return (0);
	}
}

int Board::GetOppositeDirection(int _direction)
{
	switch (_direction)
	{
		case DIRECTION_LEFT:		return (DIRECTION_RIGHT);
		case DIRECTION_RIGHT:		return (DIRECTION_LEFT);
		case DIRECTION_UP:			return (DIRECTION_DOWN);
		case DIRECTION_DOWN:		return (DIRECTION_UP);
		case DIRECTION_UP_LEFT:		return (DIRECTION_DOWN_RIGHT);
		case DIRECTION_UP_RIGHT:	return (DIRECTION_DOWN_LEFT);
		case DIRECTION_DOWN_LEFT:	return (DIRECTION_UP_RIGHT);
		case DIRECTION_DOWN_RIGHT:	return (DIRECTION_UP_LEFT);
		default:					return (0);
	}
}

void Board::GetCasePosition(BoardCase& _case, int& _x, int& _y)
{
	_x = CONVERT_POSITION_X(GET_POSITION_X(_case));
	_y = CONVERT_POSITION_Y(GET_POSITION_Y(_case));
}

#pragma endregion

void Board::CreateNewBoardCase(BoardCase& _dst, BoardCase _color, BoardCase _x, BoardCase _y,
							   BoardCase _alignEnnemy, BoardCase _alignAlly, BoardCase _surrounded)
{
	_dst = 0;
	_dst += SET_ALIGN_ENMY(_alignEnnemy);
	_dst += SET_ALIGN_ALLY(_alignAlly);
	_dst += SET_COLOR(_color);
	_dst += SET_POSITION_X(_x);
	_dst += SET_POSITION_Y(_y);
	_dst += _surrounded;
}